package org.simplesocket.simplenettycore.protocol;

import io.netty.util.Recycler;
import lombok.Getter;
import lombok.Setter;

public class SimpleProtocol {
    
    /*
     * ----------------------------------------------------------------------------------------------
     * 长度4字节 | 魔数4字节 | 类型1字节 | 状态1字节 | sessionID32字节 | 路径长度4字节 | 路径N字节 | 数据N字节
     * ----------------------------------------------------------------------------------------------
     */
    
    /**
     * 整条消息的总长度（不包括长度占用字符在内），
     * 占用四个字节，因为我们使用的是：
     * LengthFieldBasedFrameDecoder((1024 * 1024) * 2,0,4,0,4)
     * 这个解码器默认 整条消息的长度是不包含长度本身字段在内的
     * int的最大值是 2147483647 ，2147483647个字节是 2147.483647 MB
     * 也就是说单条请求最大支持2GB
     */
    private final int length = 0;

    /**
     * 魔数，用于校验此消息是否有效，
     * 0xFADEDFAD转换为int是-86057043，
     * 占用四个字节，32位bit位，读取时使用readInt()
     * faded 褪色的；消褪的；fad 时尚
     */
    public static final int MAGIC_NUMBER = 0xFADEDFAD;

    /**
     * 连接类型，连接可以分析几大连接，
     * 在下方的枚举Type中有体现
     */
    @Getter
    @Setter
    private Type type = Type.NORMAL;
    
    /**
     * 状态，就像http中的200、500等，只不过我们这里只用了一个字节
     */
    @Getter
    @Setter
    private Status status = Status.NORMAL;

    /**
     * 用户会话ID，一般是32位的UUID不带 - 符号，
     * 用于标识用户身份，由服务器颁发，客户端如果未登录
     * 导致不存在该ID，请使用32位0代替
     */
    @Getter
    @Setter
    private String sessionID = "";
    
    /**
     * 路径长度，读取这个字段后再读取之后的不定长路径即可
     */
    private final int uriLength = 0;
    
    /**
     * 请求路径
     */
    @Getter
    @Setter
    private String uri = "";

    /**
     * 携带的数据，它是经过对称加密后的JSONString，
     * 后续需要对其进行转换为字符串解密并序列化为最终对象
     */
    @Getter
    @Setter
    private String data = "";
    
    private static final Recycler<SimpleProtocol> RECYCLER = new Recycler<>(){
        @Override
        protected SimpleProtocol newObject(Handle<SimpleProtocol> handle) {
            return new SimpleProtocol(handle);
        }
    };
    
    private final Recycler.Handle<SimpleProtocol> handle;
    
    private SimpleProtocol(Recycler.Handle<SimpleProtocol> handle) {
        this.handle = handle;
    }
    
    public static SimpleProtocol newInstance() {
        return RECYCLER.get();
    }
    
    public void recycle() {
        // 重置字段
        reset();
        handle.recycle(this);
    }
    
    private void reset(){
        this.setType(Type.NORMAL);
        this.setStatus(Status.NORMAL);
        this.setSessionID("");
        this.setUri("");
        this.setData("");
    }
    
    /**
     * 获取整个协议不包含length字段在内的数据长度，单位（字节）
     * @param data 要发送的数据字节数组
     * @return 长度
     */
    public static int getProtocolTotalLength(byte[] uri,byte[] data){
        return 4 + 1 + 1 + 32 + 4 + uri.length + data.length;
    }
    
    public static Builder builder(){
        return new Builder();
    }
    
    public static class Builder{
        private final SimpleProtocol sp;
        
        private Builder() {
            this.sp = SimpleProtocol.newInstance();
        }
        
        public Builder type(Type type){
            sp.setType(type);
            return this;
        }
        
        public Builder status(Status status){
            sp.setStatus(status);
            return this;
        }
        
        public Builder sessionID(String sessionID){
            sp.setSessionID(sessionID);
            return this;
        }
        
        public Builder uri(String uri){
            sp.setUri(uri);
            return this;
        }
        
        public Builder data(String data){
            sp.setData(data);
            return this;
        }
        
        public SimpleProtocol build(){
            return sp;
        }
    }

    @Getter
    public enum Type {
        NORMAL((byte) 0),
        CONNECTED((byte) 1),
        DISCUSS_KEYS((byte) 2),
        RECONNECT((byte) 3);
        private final byte value;
        Type(byte value) {
            this.value = value;
        }
        public static Type byValue(byte v){
            for (Type type : Type.values()) {
                if (type.value == v){
                    return type;
                }
            }
            return NORMAL;
        }
    }
    
    @Getter
    public enum Status{
        NORMAL((byte)0),
        SUCCESS((byte)1),
        FAIL((byte)2),
        ERROR((byte)3),
        UNAUTHORIZED((byte)4);
        private final byte value;
        Status(byte value) {
            this.value = value;
        }
        public static Status byValue(byte v){
            for (Status status : Status.values()) {
                if (status.value == v){
                    return status;
                }
            }
            return NORMAL;
        }
    }
}
